package com.dk.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dk.common.utils.EncryptUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;

import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;


/**
 * @version:1.00.00
 * @description:网关的过滤器，做统一身份的校验，校验token后将token中的信息取出来，加密后转发给资源服务器
 * @author:DK
 * @date:2021-06-079:32
 */
@Slf4j
@Component
public class GatewayFilter implements GlobalFilter, Ordered {

    @Autowired
    private TokenStore tokenStore;

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("进入过滤器");
        String requestUrl = exchange.getRequest().getPath().value();
        AntPathMatcher pathMatcher = new AntPathMatcher();
        //1 uaa服务所有放行
        if (pathMatcher.match("/uaa/**", requestUrl)) {
            return chain.filter(exchange);
        }
        // 2.检查token是否存在
        String token = getToken(exchange);

        if(StringUtils.isEmpty(token)){
            return noTokenMono(exchange);
        }

        try {
            // 3.判断是否是有效的token
            OAuth2AccessToken oAuth2AccessToken;
            oAuth2AccessToken = tokenStore.readAccessToken(token);
            Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
            // 4.处理token信息
            // 判断要访问哪个服务（通过请求路径），然后token中的信息scope的角色是否有我们这个服务能够访问的角色（如Role_Admin），不是的话不能访问
            // 这个操作本应该在ResourceServerConfigurer中配置的，但是gateway与web的webflux一直有冲突，不知道怎么解决就放在这了
            // 或者直接放行，将scope传到资源服务器上，在对应的服务中的拦截器再进行判断是否有对应的角色
            Set<String> scope = oAuth2AccessToken.getScope();
            // 取出用户身份信息
            String principal = (String)additionalInformation.get("user_name");
            // 获取用户权限
            List<String> authorities = (List<String>) additionalInformation.get("authorities");
            HashMap hashMap = new HashMap();
            hashMap.put("principal",principal);
            hashMap.put("authorities",authorities);
            hashMap.put("scope",scope);
            //给header里面添加值
            String base64 = EncryptUtil.encodeUTF8StringBase64(new ObjectMapper().writeValueAsString(hashMap));
            ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header("json-token", base64).build();
            ServerWebExchange build = exchange.mutate().request(tokenRequest).build();
            return chain.filter(build);
        }catch (InvalidTokenException e){
            log.info("无效的token: {}", token);
            return invalidTokenMono(exchange);
        }

    }

    /**
     * 获取token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StringUtils.isEmpty(tokenStr)) {
            return null;
        }
        // "barrer token"取出token
        String token = tokenStr.split(" ")[1];
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        return token;
    }


    /**
     * 无效的token
     */
    private Mono<Void> invalidTokenMono(ServerWebExchange exchange) throws IOException {
        HashMap hashMap = new HashMap();
        hashMap.put("status", HttpStatus.UNAUTHORIZED.value());
        hashMap.put("msg", "无效的token");
        return buildReturnMono(hashMap, exchange);
    }

    private Mono<Void> noTokenMono(ServerWebExchange exchange) throws IOException {
        HashMap hashMap = new HashMap();
        hashMap.put("status", HttpStatus.UNAUTHORIZED.value());
        hashMap.put("msg", "没有token");
        return buildReturnMono(hashMap, exchange);
    }

    private Mono<Void> buildReturnMono(HashMap hashMap, ServerWebExchange exchange) throws IOException {
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = new ObjectMapper().writeValueAsString(hashMap).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码，否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 加载此过滤器的顺序，数字越小优先级越高
     * @return 优先级
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
